import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

/**
 * 
 * Authors - Oren Bracha, Rony Lahav and Yehuda Eterman
 *
 * the PictureHandler takes care of encoding and decoding the picture 
 * also utilities for the picture transfer 
 */
public class PictureHandler {
	/**
	 * allocate space from the buffer as much as possible (and as much as asked)
	 * @param maxSize - the picture size
	 * @return
	 */
	public static int allocateRcvWin(int maxSize){
		int allocatedSpace=0;
		boolean isAllocated = false;
		
		//try to allocate the first fragment
		if(maxSize < proj350.getMSS()){
			isAllocated = proj350.getInstance().allocateRcvWinSpace(maxSize);
			if (isAllocated){
				return maxSize;
			}
		}
		
		//loop that allocates space from the buffer for the receive window
		do{
			isAllocated = proj350.getInstance().allocateRcvWinSpace(proj350.getMSS());
			if (isAllocated){
				allocatedSpace += proj350.getMSS();
			}
		}while(allocatedSpace + proj350.getMSS() < maxSize && isAllocated);
		
		//allocate part of the last MSS
		if(allocatedSpace < maxSize && isAllocated){
			isAllocated = proj350.getInstance().allocateRcvWinSpace(maxSize - allocatedSpace);
			if (isAllocated){
				allocatedSpace += maxSize - allocatedSpace;
			}
		}
		
		return allocatedSpace;
	}
	
	/**
	 * returns the segments from the picture according to the list of segments
	 */
	public static byte[][] getPartsofPicInHexa(int[] segments, byte[] picture, int picLength, int segmentSize){
		if (picLength>picture.length)
			return null;
		//get all the segments of the picture
		byte[][] wholePicInHexa = getWholePicInHexa(picture, picLength, segmentSize);		
		
		//take only the segments that the user wants
		byte[][] retArr = new byte[segments.length][];
		for(int i = 0; i< segments.length; ++i){
			retArr[i] = wholePicInHexa[segments[i]-1]; 
		}
		
		return retArr;
	}
	
	/**
	 * get range of segments from specific picture
	 */
	public static byte[][] getPartsofPicInHexa(int startSegment, int endSegment, byte[] picture, int picLength, int segmentSize){
		if (picLength>picture.length)
			return null;
		//get all the fragments of the picture
		byte[][] wholePicInHexa = getWholePicInHexa(picture, picLength, segmentSize);		
		if(endSegment > wholePicInHexa.length || startSegment < 1){
			return null;
		}
		
		//take only the segments that the user wants
		byte[][] retArr = new byte[endSegment - startSegment + 1][];
		for(int i = 0; i< endSegment - startSegment + 1; ++i){
			retArr[i] = wholePicInHexa[i+startSegment-1]; 
		}
		
		return retArr;
	}
	
	/**
	 * divide the picture to segments and turn them to hexa
	 */
	public static byte[][] getWholePicInHexa(byte[] picture, int picLength, int segmentSize){
		if (picLength>picture.length)
			return null;
		int segmentNum = ((picLength * 2) / segmentSize) + 1;
		byte[][] result = new byte[segmentNum][];
		String tempSegment = "";
		int segmentIndex = 0;
		//filling every segment with bytes
		for(int i = 0; i< picLength; ++i){
			byte picByte = picture[0];
			try{
			picByte = picture[i];
			}
			catch(Exception e){
			}
			String completeHex = extractHexFromByte(picByte);
			tempSegment += completeHex;
			//check if filled a whole segment
			if(tempSegment.length() + 1 >= segmentSize){
				result[segmentIndex] = tempSegment.getBytes();
				++segmentIndex;
				tempSegment = "";
			}
			
		}
		//the last segment will not be as large as the other segments
		result[segmentIndex] = tempSegment.getBytes();
		
		return result;
	}

	/**
	 * get the picture segments and combines them into a coherent picture
	 * @param segments
	 * @return byte array ready to be written into a file
	 */
	public static byte[] createPictureFromSegments(byte[][] segments){
		byte[]  out = new byte[segments.length * segments[0].length];
		int picIndex = 0;
		//loop to decode the bytes of the picture
		for(int i = 0; i< segments.length; ++i){
			for(int j = 0; j < segments[i].length; j+=2){
				out[picIndex] = combineByteFromHex(segments[i][j], segments[i][j+1]);
				++picIndex;
			}
		}
		//copy the array to a new proper sized array
		byte[] finalArr = new byte[picIndex];
		for(int i = 0; i< picIndex; ++i){
			finalArr[i] = out[i];
		}
		return finalArr;
	}

	/**
	 * return the byte in hex format - 2 digits
	 */
	private static String extractHexFromByte(byte picByte) {
		int lowHalf = picByte & 15;
		int highHalf = (picByte & 112) >> 4;
		if(picByte < 0){
			highHalf = highHalf | 8;
		}
		String highHex = Integer.toHexString(highHalf);
		String lowHex = Integer.toHexString(lowHalf);
		String completeHex = highHex + lowHex;
		return completeHex;
	}
	
	/**
	 * return the combination of the 2 given hex numbers
	 */
	private static byte combineByteFromHex(byte highHalf, byte lowHalf){
		byte low = asciiToByte(lowHalf);
		byte high = asciiToByte(highHalf);
		high = (byte)(high << 4);
		
		return (byte)(low | high);
	}
	
	/**
	 * Takes a byte in hexa and returns it as a regular byte
	 */
	private static byte asciiToByte(byte hex){
		if(hex > 95){
			return (byte)(hex - 'a' + 10);
		}else{
			return (byte)(hex - '0');
		}
	}
	
	/**
	 * find the size of the picture file
	 */
	public static String getImageSize(String path) throws IOException{
		File file = new File(path);
		long length = file.length();
		return Long.toString(length);
	}

	/**
	 * gets a picture ready to transfer
	 */
	public static byte[] getPictureForTransfer(String peerName) throws IOException{
		DBTuple myTuple = proj350.getMyTuple();
		String pictureFilePath = null;
		//open picture file by the peers pic name, orr by [PeerName].jpg
		if (myTuple.getPeerName().compareTo(peerName) == 0){
			pictureFilePath = myTuple.getPictureFile();
		}
		else
		{
			pictureFilePath = peerName + ".jpg";
		}
		//reading the picture file
		File picture = new File(pictureFilePath);
		FileInputStream in = new FileInputStream(picture);
		byte[] picBytes = new byte[(int) picture.length()];
		in.read(picBytes);
		
		return picBytes;
	}
	
	/**
	 * On the receiving part we take the picture and decode it to a coherent picture file
	 */
	public static void combinePictureFromParts(String peerName) throws IOException{
		DBTuple tuple = DataBase.getInstance().findTuple(peerName);
		List<String> incompletePicture = tuple.getIncompletePicture();
		byte[][] completePic = new byte[incompletePicture.size()][];
		for (int i = 0; i < incompletePicture.size(); ++i) {
			completePic[i] = incompletePicture.get(i).getBytes();
		}
		//combining the picture from the hexa form
		FileOutputStream out = new FileOutputStream(new File(tuple.getPeerName()+".jpg"));
		byte[] newPicFile = createPictureFromSegments(completePic);
		List<String> pic = tuple.getIncompletePicture();
		pic.clear();
		tuple.newSetOfPeersWithNewPic();
		//writing the picture to file
		out.write(newPicFile);
		out.flush();
		out.close();
	}
	
}